package cn.sytton.taffe.auth.service.impl;

import cn.sytton.taffe.auth.entity.AuthUserEntity;
import cn.sytton.taffe.auth.mapper.SecurityMapper;
import cn.sytton.taffe.auth.service.ILoginService;
import cn.sytton.taffe.auth.service.ISSOService;
import cn.sytton.taffe.auth.service.security.UserDetailFactory;
import cn.sytton.taffe.framework.exception.BizException;
import cn.sytton.taffe.framework.properties.auth.AuthProperties;
import cn.sytton.taffe.framework.security.entity.LoginUser;
import cn.sytton.taffe.framework.security.user.UserAuthenticationToken;
import cn.sytton.taffe.framework.util.JsonUtil;
import cn.sytton.taffe.framework.util.WebClientUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ExchangeFilterFunctions;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Objects;

/**
 * SSO服务层
 *
 * @author skyrock
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class SSOService implements ISSOService {

    @Resource
    private AuthProperties authProperties;
    @Resource
    private SecurityMapper securityMapper;
    @Resource
    private UserDetailFactory userDetailFactory;
    @Resource
    private AuthenticationManager authenticationManager;
    @Resource
    private ILoginService loginService;

    @Override
    public String verifyCode(String authorizationCode, String redirectUri) {
        String ssoToken = getSSOToken(authorizationCode, redirectUri);
        String loginName = getSSOProfile(ssoToken);
        AuthUserEntity authUser = securityMapper.findUserByLoginName(loginName);
        if (Objects.nonNull(authUser)) {
            LoginUser loginUser = userDetailFactory.create(authUser.getLoginName(), authUser);
            Authentication authentication =  authenticationManager.authenticate(new UserAuthenticationToken(loginUser));
            return loginService.generateToken(authentication);
        } else {
            throw new BizException("统一用户["+loginName+"]不存在");
        }
    }

    private String getSSOToken(String authorizationCode, String redirectUri) {
        MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
        data.add("redirect_uri", redirectUri);
        data.add("grant_type", "authorization_code");
        data.add("code", authorizationCode);

        String response = request(authProperties.getSso().getTokenUrl(), data);

        if (StringUtils.hasText(response)) {
            Map<String, Object> result = JsonUtil.getEntity(response);
            /*
            {
                "access_token": "044fd75d-c0f7-4bc4-ab81-41d5c4e4f845",
                "refresh_token": "6f6492e3-e72b-4b58-9231-38bb3de9fe06",
                "scope": "openid",
                "id_token": "1b5fa99a-f4b9-4637-b1a3-bed521ec9905",
                "token_type": "Bearer",
                "expires_in": 3599
            }
             */
            if (result != null && result.containsKey("access_token")) {
                return result.get("access_token").toString();
            } else {
                log.error("统一用户认证错误---"+response);
                throw new BizException("统一用户认证错误");
            }
        } else {
            throw new BizException("统一用户认证错误");
        }
    }

    private String getSSOProfile(String accessToken) {
        MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
        data.add("token", accessToken);

        String response = request(authProperties.getSso().getProfileUrl(), data);

        if (StringUtils.hasText(response)) {
            Map<String, Object> result = JsonUtil.getEntity(response);
            /*
                {
                  "active": true,
                  "sub": "admin",
                  "aud": [
                    "qq"
                  ],
                  "azp": "qq",
                  "iss": "http://localhost:8080",
                  "exp": 1737624066,
                  "iat": 1737622266,
                  "jti": "1b2f127f-e6e9-40b8-a465-1c32fdd8669e",
                  "client_id": "qq"
                }
             */
            if (result != null && result.containsKey("sub")) {
                return result.get("sub").toString();
            } else {
                log.error("获取统一用户错误---"+response);
                throw new BizException("获取统一用户错误");
            }
        } else {
            throw new BizException("获取统一用户错误");
        }
    }

    private String request(String uri, MultiValueMap<String, String> data) {
        Mono<String> mono = WebClientUtil.getDefaultWebClientBuilder()
                .filter(ExchangeFilterFunctions.basicAuthentication(authProperties.getSso().getClientId(), authProperties.getSso().getClientSecret()))
                .build()
                .post()
                .uri(uri)
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .body(BodyInserters.fromFormData(data))
                .retrieve()
                .bodyToMono(String.class);
        try {
            return mono.block();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new BizException("统一认证异常");
        }
    }
}
